﻿#region 참조 네임스페이스

using Skying.Windows.API;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

#endregion

namespace Skying.Windows.Hook
{
	/// <summary>
	/// 글로벌 마우스 이벤트와 글로버 키보드 이벤트에 대한 후크 인터페이스를 제공한다.
	/// </summary>
	public class HookBroker
	{
		#region 변수

		/// <summary>
		/// 마우스 후크 프로시져에 대한 핸들을 나타낸다.
		/// </summary>
		private static IntPtr _mouseHookHandle;

		/// <summary>
		/// 키보드 후크 프로시져에 대한 핸들을 나타낸다.
		/// </summary>
		private static IntPtr _keyboardHookHandle;

		#endregion

		#region 마우스 이벤트

		/// <summary>
		/// 마우스가 클릭됐을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event MouseEventHandler MouseClick;

		/// <summary>
		/// 마우스가 두번 클릭됐을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event MouseEventHandler MouseDoubleClick;

		/// <summary>
		/// 마우스가 눌렸을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event MouseEventHandler MouseDown;

		/// <summary>
		/// 마우스가 눌렸다가 떼어졌을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event MouseEventHandler MouseUp;

		/// <summary>
		/// 마우스의 휠이 움직였을 때 발생하는 이벤트를 나타낸다. 
		/// </summary>
		public static event MouseEventHandler MouseWheel;

		/// <summary>
		/// 마우스가 이동했을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event MouseEventHandler MouseMove;

		#endregion

		#region 마우스 이벤트 후크

		/// <summary>
		/// 글로벌 마우스 이벤트에 대한 후크를 설정한다.
		/// </summary>
		public static void HookGlobalMouseEvents()
		{
			if (_mouseHookHandle.Equals(IntPtr.Zero))
			{
				_mouseHookHandle = User32.SetWindowsHookEx(User32.WindowHookId.WH_MOUSE_LL,
					(int nCode, int wParam, IntPtr lParam) =>
						{
							// lParam 파라메터로부터 MouseLLHookStruct 구조체 정보를 가져온다.
							User32.MouseLLHookStruct mouseHookStruct = (User32.MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(User32.MouseLLHookStruct));

							// 대상 마우스의 버튼을 나타낸다.
							MouseButtons button = MouseButtons.None;
							// 마우스 휠의 회전 수를 나타낸다.
							short delta = 0;
							// 마우스 버튼의 클릭 회수를 나타낸다.
							int clicks = 0;
							// 마우스 버튼이 눌린 상태인지를 나타낸다.
							bool down = false;
							// 마우스 버튼이 눌렸다가 떼어진 상태인지를 나타낸다.
							bool up = false;

							// wParam으로부터 마우스 이벤트의 유형을 알아낸다.
							Winuser.MouseInputNotifications mouseInputNotification = (Winuser.MouseInputNotifications)wParam;

							switch (mouseInputNotification)
							{
								case Winuser.MouseInputNotifications.WM_LBUTTONDOWN:

									down = true;
									button = MouseButtons.Left;
									clicks = 1;

									break;

								case Winuser.MouseInputNotifications.WM_LBUTTONUP:

									up = true;
									button = MouseButtons.Left;
									clicks = 1;

									break;

								case Winuser.MouseInputNotifications.WM_LBUTTONDBLCLK:

									button = MouseButtons.Left;
									clicks = 2;

									break;

								case Winuser.MouseInputNotifications.WM_RBUTTONDOWN:

									down = true;
									button = MouseButtons.Right;
									clicks = 1;

									break;

								case Winuser.MouseInputNotifications.WM_RBUTTONUP:

									up = true;
									button = MouseButtons.Right;
									clicks = 1;

									break;

								case Winuser.MouseInputNotifications.WM_RBUTTONDBLCLK:

									button = MouseButtons.Right;
									clicks = 2;

									break;

								case Winuser.MouseInputNotifications.WM_MOUSEWHEEL:

									delta = (short)((mouseHookStruct.MouseData >> 16) & 0xffff);

									break;
							}

							MouseEventExtensionArgs eventArg = new MouseEventExtensionArgs(button, clicks, mouseHookStruct.Point.X, mouseHookStruct.Point.Y, delta);

							if (MouseUp != null && up)
							{
								MouseUp.Invoke(null, eventArg);
							}

							if (MouseDown != null && down)
							{
								MouseDown.Invoke(null, eventArg);
							}

							if (MouseDoubleClick != null && clicks > 0)
							{
								MouseDoubleClick.Invoke(null, eventArg);
							}

							if (MouseClick != null && clicks > 0)
							{
								MouseClick.Invoke(null, eventArg);
							}

							if (MouseWheel != null && delta != 0)
							{
								MouseWheel.Invoke(null, eventArg);
							}

							if (MouseMove != null && mouseInputNotification == Winuser.MouseInputNotifications.WM_MOUSEMOVE)
							{
								MouseMove.Invoke(null, eventArg);
							}

							if (mouseInputNotification != Winuser.MouseInputNotifications.WM_MOUSEMOVE)
							{
								Console.WriteLine("MouseInput : {0}, Button : {1}, Click : {2}, Delta : {3}", mouseInputNotification, button, clicks, delta);
								Console.WriteLine("Point : {0}, Data : {1}, Flags : {2}, ExtraInfo : {3}", mouseHookStruct.Point, mouseHookStruct.MouseData, mouseHookStruct.Flags, mouseHookStruct.ExtraInfo);
							}

							return User32.CallNextHookEx(_mouseHookHandle, nCode, wParam, lParam);
						},
					Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
			}
		}

		/// <summary>
		/// 글로벌 마우스 이벤트에 대한 후크를 해제한다.
		/// </summary>
		public static void UnhookGlobalMouseEvents()
		{
			if (!_mouseHookHandle.Equals(IntPtr.Zero))
			{
				if (User32.UnhookWindowsHookEx(_mouseHookHandle))
				{
					_mouseHookHandle = IntPtr.Zero;
				}
			}

			MouseClick = null;
			MouseDoubleClick = null;
			MouseDown = null;
			MouseUp = null;
			MouseWheel = null;
		}

		#endregion

		#region 키보드 이벤트

		/// <summary>
		/// 키가 눌렸을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event KeyEventHandler KeyDown;

		/// <summary>
		/// 키가 눌렸다가 떼어졌을 때 발생하는 이벤트를 나타낸다.
		/// </summary>
		public static event KeyEventHandler KeyUp;

		/// <summary>
		/// 
		/// </summary>
		public static event KeyPressEventHandler KeyPress;

		#endregion

		#region 키보드 이벤트 후크

		/// <summary>
		/// 글로벌 키보드 이벤트에 대한 후크를 설정한다.
		/// </summary>
		public static void HookGlobalKeyboardEvents()
		{
			if (_keyboardHookHandle.Equals(IntPtr.Zero))
			{
				_keyboardHookHandle = User32.SetWindowsHookEx(User32.WindowHookId.WH_KEYBOARD_LL,
					(int nCode, int wParam, IntPtr lParam) =>
					{
						User32.KbdLLHookStruct keyboardHookStruct = (User32.KbdLLHookStruct)Marshal.PtrToStructure(lParam, typeof(User32.KbdLLHookStruct));

						Winuser.KeyboardInputNotifications keyboardInputNotification = (Winuser.KeyboardInputNotifications)wParam;

						if (KeyDown != null &&
							(keyboardInputNotification == Winuser.KeyboardInputNotifications.WM_KEYDOWN ||
							keyboardInputNotification == Winuser.KeyboardInputNotifications.WM_SYSKEYDOWN))
						{
							Keys key = (Keys)keyboardHookStruct.VirtualKeyCode;

							KeyEventArgs eventArg = new KeyEventArgs(key);

							KeyDown.Invoke(null, eventArg);
						}

						if (KeyPress != null &&
							keyboardInputNotification == Winuser.KeyboardInputNotifications.WM_KEYUP)
						{
							bool shiftDown = (User32.GetKeyState((int)Winuser.VirtualKeyCodes.VK_SHIFT) & 0x80) == 0x80 ? true : false;
							bool capslockDown = User32.GetKeyState((int)Winuser.VirtualKeyCodes.VK_CAPITAL) != 0 ? true : false;

							byte[] keyState = new byte[256];

							User32.GetKeyboardState(out keyState);

							byte[] inBuffer = new byte[2];


						}

						//// raise KeyPress
						//if (s_KeyPress != null && wParam == WM_KEYDOWN)
						//{
						//	bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
						//	bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);

						//	byte[] keyState = new byte[256];
						//	GetKeyboardState(keyState);
						//	byte[] inBuffer = new byte[2];
						//	if (ToAscii(MyKeyboardHookStruct.VirtualKeyCode,
						//			  MyKeyboardHookStruct.ScanCode,
						//			  keyState,
						//			  inBuffer,
						//			  MyKeyboardHookStruct.Flags) == 1)
						//	{
						//		char key = (char)inBuffer[0];
						//		if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
						//		KeyPressEventArgs e = new KeyPressEventArgs(key);
						//		s_KeyPress.Invoke(null, e);
						//		handled = handled || e.Handled;
						//	}
						//}

						//// raise KeyUp
						//if (s_KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
						//{
						//	Keys keyData = (Keys)MyKeyboardHookStruct.VirtualKeyCode;
						//	KeyEventArgs e = new KeyEventArgs(keyData);
						//	s_KeyUp.Invoke(null, e);
						//	handled = handled || e.Handled;
						//}

						return User32.CallNextHookEx(_keyboardHookHandle, nCode, wParam, lParam);
					},
				Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
			}
		}

		/// <summary>
		/// 글로벌 키보드 이벤트에 대한 후크를 해제한다.
		/// </summary>
		public static void UnhookGlobalKeyboardEvents()
		{

		}

		#endregion
	}
}